home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / app / fuzzy_select.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-05-08  |  16.9 KB  |  684 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <stdlib.h>
  22.  
  23. #include <gtk/gtk.h>
  24.  
  25. #include "apptypes.h"
  26.  
  27. #include "appenv.h"
  28. #include "boundary.h"
  29. #include "cursorutil.h"
  30. #include "draw_core.h"
  31. #include "drawable.h"
  32. #include "edit_selection.h"
  33. #include "floating_sel.h"
  34. #include "fuzzy_select.h"
  35. #include "gimage_mask.h"
  36. #include "gimprc.h"
  37. #include "gimpui.h"
  38. #include "gdisplay.h"
  39. #include "rect_select.h"
  40. #include "selection_options.h"
  41.  
  42. #include "tile.h"            /* ick. */
  43.  
  44. #include "libgimp/gimpmath.h"
  45. #include "libgimp/gimpintl.h"
  46.  
  47. /*  the fuzzy selection structures  */
  48.  
  49. typedef struct _FuzzySelect FuzzySelect;
  50. struct _FuzzySelect
  51. {
  52.   DrawCore *core;       /*  Core select object                      */
  53.  
  54.   gint      op;         /*  selection operation (ADD, SUB, etc)     */
  55.  
  56.   gint      current_x;  /*  these values are updated on every motion event  */
  57.   gint      current_y;  /*  (enables immediate cursor updating on modifier
  58.              *   key events).  */
  59.  
  60.   gint      x, y;             /*  Point from which to execute seed fill  */
  61.   gint      first_x;          /*                                         */
  62.   gint      first_y;          /*  variables to keep track of sensitivity */
  63.   gdouble   first_threshold;  /* initial value of threshold slider   */
  64. };
  65.  
  66.  
  67. /*  the fuzzy selection tool options  */
  68. static SelectionOptions  *fuzzy_options = NULL;
  69.  
  70. /*  XSegments which make up the fuzzy selection boundary  */
  71. static GdkSegment *segs     = NULL;
  72. static gint        num_segs = 0;
  73.  
  74. Channel * fuzzy_mask = NULL;
  75.  
  76.  
  77. /*  fuzzy select action functions  */
  78. static void   fuzzy_select_button_press    (Tool *, GdkEventButton *, gpointer);
  79. static void   fuzzy_select_button_release  (Tool *, GdkEventButton *, gpointer);
  80. static void   fuzzy_select_motion          (Tool *, GdkEventMotion *, gpointer);
  81. static void   fuzzy_select_control         (Tool *, ToolAction,       gpointer);
  82.  
  83. static void   fuzzy_select_draw            (Tool *);
  84.  
  85. /*  fuzzy select action functions  */
  86. static GdkSegment * fuzzy_select_calculate (Tool *, void *, int *);
  87.  
  88.  
  89. /*************************************/
  90. /*  Fuzzy selection apparatus  */
  91.  
  92. static gint
  93. is_pixel_sufficiently_different (guchar   *col1, 
  94.                  guchar   *col2,
  95.                  gboolean  antialias, 
  96.                  gint      threshold, 
  97.                  gint      bytes,
  98.                  gboolean  has_alpha)
  99. {
  100.   gint diff;
  101.   gint max;
  102.   gint b;
  103.   gint alpha;
  104.  
  105.   max = 0;
  106.   alpha = (has_alpha) ? bytes - 1 : bytes;
  107.  
  108.   /*  if there is an alpha channel, never select transparent regions  */
  109.   if (has_alpha && col2[alpha] == 0)
  110.     return 0;
  111.  
  112.   for (b = 0; b < bytes; b++)
  113.     {
  114.       diff = col1[b] - col2[b];
  115.       diff = abs (diff);
  116.       if (diff > max)
  117.     max = diff;
  118.     }
  119.  
  120.   if (antialias)
  121.     {
  122.       float aa;
  123.  
  124.       aa = 1.5 - ((float) max / threshold);
  125.       if (aa <= 0)
  126.     return 0;
  127.       else if (aa < 0.5)
  128.     return (unsigned char) (aa * 512);
  129.       else
  130.     return 255;
  131.     }
  132.   else
  133.     {
  134.       if (max > threshold)
  135.     return 0;
  136.       else
  137.     return 255;
  138.     }
  139. }
  140.  
  141. static void
  142. ref_tiles (TileManager  *src, 
  143.        TileManager  *mask, 
  144.        Tile        **s_tile, 
  145.        Tile        **m_tile,
  146.        gint          x, 
  147.        gint          y, 
  148.        guchar      **s, 
  149.        guchar      **m)
  150. {
  151.   if (*s_tile != NULL)
  152.     tile_release (*s_tile, FALSE);
  153.   if (*m_tile != NULL)
  154.     tile_release (*m_tile, TRUE);
  155.  
  156.   *s_tile = tile_manager_get_tile (src, x, y, TRUE, FALSE);
  157.   *m_tile = tile_manager_get_tile (mask, x, y, TRUE, TRUE);
  158.  
  159.   *s = tile_data_pointer (*s_tile, x % TILE_WIDTH, y % TILE_HEIGHT);
  160.   *m = tile_data_pointer (*m_tile, x % TILE_WIDTH, y % TILE_HEIGHT);
  161. }
  162.  
  163. static int
  164. find_contiguous_segment (guchar      *col, 
  165.              PixelRegion *src,
  166.              PixelRegion *mask, 
  167.              gint         width, 
  168.              gint         bytes,
  169.              gboolean     has_alpha, 
  170.              gboolean     antialias, 
  171.              gint         threshold,
  172.              gint         initial, 
  173.              gint        *start, 
  174.              gint        *end)
  175. {
  176.   guchar *s;
  177.   guchar *m;
  178.   guchar  diff;
  179.   Tile   *s_tile = NULL;
  180.   Tile   *m_tile = NULL;
  181.  
  182.   ref_tiles (src->tiles, mask->tiles, &s_tile, &m_tile, src->x, src->y, &s, &m);
  183.  
  184.   /* check the starting pixel */
  185.   if (! (diff = is_pixel_sufficiently_different (col, s, antialias,
  186.                          threshold, bytes, has_alpha)))
  187.     {
  188.       tile_release (s_tile, FALSE);
  189.       tile_release (m_tile, TRUE);
  190.       return FALSE;
  191.     }
  192.  
  193.   *m-- = diff;
  194.   s -= bytes;
  195.   *start = initial - 1;
  196.  
  197.   while (*start >= 0 && diff)
  198.     {
  199.       if (! ((*start + 1) % TILE_WIDTH))
  200.     ref_tiles (src->tiles, mask->tiles, &s_tile, &m_tile, *start, src->y, &s, &m);
  201.  
  202.       diff = is_pixel_sufficiently_different (col, s, antialias,
  203.                           threshold, bytes, has_alpha);
  204.       if ((*m-- = diff))
  205.     {
  206.       s -= bytes;
  207.       (*start)--;
  208.     }
  209.     }
  210.  
  211.   diff = 1;
  212.   *end = initial + 1;
  213.   if (*end % TILE_WIDTH && *end < width)
  214.     ref_tiles (src->tiles, mask->tiles, &s_tile, &m_tile, *end, src->y, &s, &m);
  215.  
  216.   while (*end < width && diff)
  217.     {
  218.       if (! (*end % TILE_WIDTH))
  219.     ref_tiles (src->tiles, mask->tiles, &s_tile, &m_tile, *end, src->y, &s, &m);
  220.  
  221.       diff = is_pixel_sufficiently_different (col, s, antialias,
  222.                           threshold, bytes, has_alpha);
  223.       if ((*m++ = diff))
  224.     {
  225.       s += bytes;
  226.       (*end)++;
  227.     }
  228.     }
  229.  
  230.   tile_release (s_tile, FALSE);
  231.   tile_release (m_tile, TRUE);
  232.   return TRUE;
  233. }
  234.  
  235. static void
  236. find_contiguous_region_helper (PixelRegion *mask, 
  237.                    PixelRegion *src,
  238.                    gboolean     has_alpha, 
  239.                    gboolean     antialias, 
  240.                    gint         threshold, 
  241.                    gboolean     indexed,
  242.                    gint         x, 
  243.                    gint         y, 
  244.                    guchar      *col)
  245. {
  246.   gint start, end, i;
  247.   gint val;
  248.   gint bytes;
  249.  
  250.   Tile *tile;
  251.  
  252.   if (x < 0 || x >= src->w) return;
  253.   if (y < 0 || y >= src->h) return;
  254.  
  255.   tile = tile_manager_get_tile (mask->tiles, x, y, TRUE, FALSE);
  256.   val = *(guchar *)(tile_data_pointer (tile, 
  257.                        x % TILE_WIDTH, y % TILE_HEIGHT));
  258.   tile_release (tile, FALSE);
  259.   if (val != 0)
  260.     return;
  261.  
  262.   src->x = x;
  263.   src->y = y;
  264.  
  265.   bytes = src->bytes;
  266.   if(indexed)
  267.     {
  268.       bytes = has_alpha ? 4 : 3;
  269.     }
  270.  
  271.   if (! find_contiguous_segment (col, src, mask, src->w,
  272.                  src->bytes, has_alpha,
  273.                  antialias, threshold, x, &start, &end))
  274.     return;
  275.  
  276.   for (i = start + 1; i < end; i++)
  277.     {
  278.       find_contiguous_region_helper (mask, src, has_alpha, antialias, 
  279.                      threshold, indexed, i, y - 1, col);
  280.       find_contiguous_region_helper (mask, src, has_alpha, antialias, 
  281.                      threshold, indexed, i, y + 1, col);
  282.     }
  283. }
  284.  
  285. Channel *
  286. find_contiguous_region (GImage       *gimage, 
  287.             GimpDrawable *drawable, 
  288.             gboolean      antialias,
  289.             gint          threshold, 
  290.             gint          x, 
  291.             gint          y, 
  292.             gboolean      sample_merged)
  293. {
  294.   PixelRegion srcPR, maskPR;
  295.   Channel  *mask;
  296.   guchar   *start;
  297.   gboolean  has_alpha;
  298.   gboolean  indexed;
  299.   gint      type;
  300.   gint      bytes;
  301.   Tile     *tile;
  302.  
  303.   if (sample_merged)
  304.     {
  305.       pixel_region_init (&srcPR, gimage_composite (gimage), 0, 0,
  306.              gimage->width, gimage->height, FALSE);
  307.       type = gimage_composite_type (gimage);
  308.       has_alpha = (type == RGBA_GIMAGE ||
  309.            type == GRAYA_GIMAGE ||
  310.            type == INDEXEDA_GIMAGE);
  311.     }
  312.   else
  313.     {
  314.       pixel_region_init (&srcPR, drawable_data (drawable), 0, 0,
  315.              drawable_width (drawable), drawable_height (drawable),
  316.              FALSE);
  317.       has_alpha = drawable_has_alpha (drawable);
  318.     }
  319.   indexed = drawable_indexed (drawable);
  320.   bytes = drawable_bytes (drawable);
  321.   
  322.   if (indexed)
  323.     {
  324.       bytes = has_alpha ? 4 : 3;
  325.     }
  326.   mask = channel_new_mask (gimage, srcPR.w, srcPR.h);
  327.   pixel_region_init (&maskPR, drawable_data (GIMP_DRAWABLE(mask)), 0, 0, 
  328.              drawable_width (GIMP_DRAWABLE(mask)), 
  329.              drawable_height (GIMP_DRAWABLE(mask)), 
  330.              TRUE);
  331.  
  332.   tile = tile_manager_get_tile (srcPR.tiles, x, y, TRUE, FALSE);
  333.   if (tile)
  334.     {
  335.       start = tile_data_pointer (tile, x%TILE_WIDTH, y%TILE_HEIGHT);
  336.  
  337.       find_contiguous_region_helper (&maskPR, &srcPR, has_alpha, antialias, 
  338.                      threshold, bytes, x, y, start);
  339.  
  340.       tile_release (tile, FALSE);
  341.     }
  342.  
  343.   return mask;
  344. }
  345.  
  346. void
  347. fuzzy_select (GImage       *gimage, 
  348.           GimpDrawable *drawable, 
  349.           gint          op, 
  350.           gboolean      feather,
  351.           gdouble       feather_radius)
  352. {
  353.   gint off_x, off_y;
  354.  
  355.   /*  if applicable, replace the current selection  */
  356.   if (op == REPLACE)
  357.     gimage_mask_clear (gimage);
  358.   else
  359.     gimage_mask_undo (gimage);
  360.  
  361.   if (drawable)     /* NULL if sample_merged is active */
  362.     drawable_offsets (drawable, &off_x, &off_y);
  363.   else
  364.     off_x = off_y = 0;
  365.   
  366.   if (feather)
  367.     channel_feather (fuzzy_mask, gimage_get_mask (gimage),
  368.              feather_radius,
  369.              feather_radius,
  370.              op, off_x, off_y);
  371.   else
  372.     channel_combine_mask (gimage_get_mask (gimage),
  373.               fuzzy_mask, op, off_x, off_y);
  374.  
  375.   /*  free the fuzzy region struct  */
  376.   channel_delete (fuzzy_mask);
  377.   fuzzy_mask = NULL;
  378. }
  379.  
  380. /*  fuzzy select action functions  */
  381.  
  382. static void
  383. fuzzy_select_button_press (Tool           *tool, 
  384.                GdkEventButton *bevent,
  385.                gpointer        gdisp_ptr)
  386. {
  387.   GDisplay    *gdisp;
  388.   FuzzySelect *fuzzy_sel;
  389.  
  390.   gdisp = (GDisplay *) gdisp_ptr;
  391.   fuzzy_sel = (FuzzySelect *) tool->private;
  392.  
  393.   fuzzy_sel->x = bevent->x;
  394.   fuzzy_sel->y = bevent->y;
  395.   fuzzy_sel->first_x = fuzzy_sel->x;
  396.   fuzzy_sel->first_y = fuzzy_sel->y;
  397.   fuzzy_sel->first_threshold = fuzzy_options->threshold;
  398.  
  399.   gdk_pointer_grab (gdisp->canvas->window, FALSE,
  400.             GDK_POINTER_MOTION_HINT_MASK |
  401.             GDK_BUTTON1_MOTION_MASK |
  402.             GDK_BUTTON_RELEASE_MASK,
  403.             NULL, NULL, bevent->time);
  404.  
  405.   tool->state = ACTIVE;
  406.   tool->gdisp_ptr = gdisp;
  407.  
  408.   if (fuzzy_sel->op == SELECTION_MOVE_MASK)
  409.     {
  410.       init_edit_selection (tool, gdisp_ptr, bevent, EDIT_MASK_TRANSLATE);
  411.       return;
  412.     }
  413.   else if (fuzzy_sel->op == SELECTION_MOVE)
  414.     {
  415.       init_edit_selection (tool, gdisp_ptr, bevent, EDIT_MASK_TO_LAYER_TRANSLATE);
  416.       return;
  417.     }
  418.  
  419.   /*  calculate the region boundary  */
  420.   segs = fuzzy_select_calculate (tool, gdisp_ptr, &num_segs);
  421.  
  422.   draw_core_start (fuzzy_sel->core,
  423.            gdisp->canvas->window,
  424.            tool);
  425. }
  426.  
  427. static void
  428. fuzzy_select_button_release (Tool           *tool, 
  429.                  GdkEventButton *bevent,
  430.                  gpointer        gdisp_ptr)
  431. {
  432.   FuzzySelect  *fuzzy_sel;
  433.   GDisplay     *gdisp;
  434.   GimpDrawable *drawable;
  435.  
  436.   gdisp = (GDisplay *) gdisp_ptr;
  437.   fuzzy_sel = (FuzzySelect *) tool->private;
  438.  
  439.   gdk_pointer_ungrab (bevent->time);
  440.   gdk_flush ();
  441.  
  442.   draw_core_stop (fuzzy_sel->core, tool);
  443.   tool->state = INACTIVE;
  444.  
  445.   /*  First take care of the case where the user "cancels" the action  */
  446.   if (! (bevent->state & GDK_BUTTON3_MASK))
  447.     {
  448.       if (fuzzy_sel->op == SELECTION_ANCHOR)
  449.     {
  450.       /*  If there is a floating selection, anchor it  */
  451.       if (gimage_floating_sel (gdisp->gimage))
  452.         floating_sel_anchor (gimage_floating_sel (gdisp->gimage));
  453.       /*  Otherwise, clear the selection mask  */
  454.       else
  455.         gimage_mask_clear (gdisp->gimage);
  456.  
  457.       gdisplays_flush ();
  458.       return;
  459.     }
  460.  
  461.       drawable = (fuzzy_options->sample_merged ?
  462.           NULL : gimage_active_drawable (gdisp->gimage));
  463.  
  464.       fuzzy_select (gdisp->gimage, drawable, fuzzy_sel->op,
  465.             fuzzy_options->feather, 
  466.             fuzzy_options->feather_radius);
  467.       gdisplays_flush ();
  468.     }
  469.  
  470.   /*  If the segment array is allocated, free it  */
  471.   if (segs)
  472.     g_free (segs);
  473.   segs = NULL;
  474. }
  475.  
  476. static void
  477. fuzzy_select_motion (Tool           *tool, 
  478.              GdkEventMotion *mevent, 
  479.              gpointer        gdisp_ptr)
  480. {
  481.   FuzzySelect *fuzzy_sel;
  482.   GdkSegment  *new_segs;
  483.   gint    num_new_segs;
  484.   gint    diff_x, diff_y;
  485.   gdouble diff;
  486.  
  487.   static guint last_time = 0;
  488.  
  489.   fuzzy_sel = (FuzzySelect *) tool->private;
  490.  
  491.   /*  needed for immediate cursor update on modifier event  */
  492.   fuzzy_sel->current_x = mevent->x;
  493.   fuzzy_sel->current_y = mevent->y;
  494.  
  495.   if (tool->state != ACTIVE)
  496.     return;
  497.  
  498.   /* don't let the events come in too fast, ignore below a delay of 100 ms */
  499.   if (ABS (mevent->time - last_time) < 100)
  500.     return;
  501.   
  502.   last_time = mevent->time;
  503.  
  504.   diff_x = mevent->x - fuzzy_sel->first_x;
  505.   diff_y = mevent->y - fuzzy_sel->first_y;
  506.  
  507.   diff = ((ABS (diff_x) > ABS (diff_y)) ? diff_x : diff_y) / 2.0;
  508.  
  509.   gtk_adjustment_set_value (GTK_ADJUSTMENT (fuzzy_options->threshold_w), 
  510.                 fuzzy_sel->first_threshold + diff);
  511.       
  512.   /*  calculate the new fuzzy boundary  */
  513.   new_segs = fuzzy_select_calculate (tool, gdisp_ptr, &num_new_segs);
  514.  
  515.   /*  stop the current boundary  */
  516.   draw_core_pause (fuzzy_sel->core, tool);
  517.  
  518.   /*  make sure the XSegment array is freed before we assign the new one  */
  519.   if (segs)
  520.     g_free (segs);
  521.   segs = new_segs;
  522.   num_segs = num_new_segs;
  523.  
  524.   /*  start the new boundary  */
  525.   draw_core_resume (fuzzy_sel->core, tool);
  526. }
  527.  
  528.  
  529. static GdkSegment *
  530. fuzzy_select_calculate (Tool *tool, 
  531.             void *gdisp_ptr, 
  532.             gint *nsegs)
  533. {
  534.   PixelRegion   maskPR;
  535.   FuzzySelect  *fuzzy_sel;
  536.   GDisplay     *gdisp;
  537.   Channel      *new;
  538.   GdkSegment   *segs;
  539.   BoundSeg     *bsegs;
  540.   GimpDrawable *drawable;
  541.   gint     i;
  542.   gint     x, y;
  543.   gboolean use_offsets;
  544.  
  545.   fuzzy_sel = (FuzzySelect *) tool->private;
  546.   gdisp = (GDisplay *) gdisp_ptr;
  547.   drawable = gimage_active_drawable (gdisp->gimage);
  548.  
  549.   gimp_add_busy_cursors ();
  550.  
  551.   use_offsets = fuzzy_options->sample_merged ? FALSE : TRUE;
  552.  
  553.   gdisplay_untransform_coords (gdisp, fuzzy_sel->x,
  554.                    fuzzy_sel->y, &x, &y, FALSE, use_offsets);
  555.  
  556.   new = find_contiguous_region (gdisp->gimage, drawable, 
  557.                 fuzzy_options->antialias,
  558.                 fuzzy_options->threshold, x, y, 
  559.                 fuzzy_options->sample_merged);
  560.  
  561.   if (fuzzy_mask)
  562.     channel_delete (fuzzy_mask);
  563.   fuzzy_mask = channel_ref (new);
  564.  
  565.   /*  calculate and allocate a new XSegment array which represents the boundary
  566.    *  of the color-contiguous region
  567.    */
  568.   pixel_region_init (&maskPR, drawable_data (GIMP_DRAWABLE (fuzzy_mask)), 0, 0, 
  569.              drawable_width (GIMP_DRAWABLE (fuzzy_mask)), 
  570.              drawable_height (GIMP_DRAWABLE (fuzzy_mask)), 
  571.              FALSE);
  572.   bsegs = find_mask_boundary (&maskPR, nsegs, WithinBounds,
  573.                   0, 0,
  574.                   drawable_width (GIMP_DRAWABLE (fuzzy_mask)),
  575.                   drawable_height (GIMP_DRAWABLE (fuzzy_mask)));
  576.  
  577.   segs = g_new (GdkSegment, *nsegs);
  578.  
  579.   for (i = 0; i < *nsegs; i++)
  580.     {
  581.       gdisplay_transform_coords (gdisp, bsegs[i].x1, bsegs[i].y1, &x, &y, use_offsets);
  582.       segs[i].x1 = x;  segs[i].y1 = y;
  583.       gdisplay_transform_coords (gdisp, bsegs[i].x2, bsegs[i].y2, &x, &y, use_offsets);
  584.       segs[i].x2 = x;  segs[i].y2 = y;
  585.     }
  586.  
  587.   /*  free boundary segments  */
  588.   g_free (bsegs);
  589.  
  590.   gimp_remove_busy_cursors (NULL);
  591.  
  592.   return segs;
  593. }
  594.  
  595. static void
  596. fuzzy_select_draw (Tool *tool)
  597. {
  598.   FuzzySelect *fuzzy_sel;
  599.  
  600.   fuzzy_sel = (FuzzySelect *) tool->private;
  601.  
  602.   if (segs)
  603.     gdk_draw_segments (fuzzy_sel->core->win, fuzzy_sel->core->gc, segs, num_segs);
  604. }
  605.  
  606. static void
  607. fuzzy_select_control (Tool       *tool,
  608.               ToolAction  action,
  609.               gpointer    gdisp_ptr)
  610. {
  611.   FuzzySelect *fuzzy_sel;
  612.  
  613.   fuzzy_sel = (FuzzySelect *) tool->private;
  614.  
  615.   switch (action)
  616.     {
  617.     case PAUSE :
  618.       draw_core_pause (fuzzy_sel->core, tool);
  619.       break;
  620.  
  621.     case RESUME :
  622.       draw_core_resume (fuzzy_sel->core, tool);
  623.       break;
  624.  
  625.     case HALT :
  626.       draw_core_stop (fuzzy_sel->core, tool);
  627.       break;
  628.  
  629.     default:
  630.       break;
  631.     }
  632. }
  633.  
  634. static void
  635. fuzzy_select_options_reset (void)
  636. {
  637.   selection_options_reset (fuzzy_options);
  638. }
  639.  
  640. Tool *
  641. tools_new_fuzzy_select (void)
  642. {
  643.   Tool        *tool;
  644.   FuzzySelect *private;
  645.  
  646.   /*  The tool options  */
  647.   if (! fuzzy_options)
  648.     {
  649.       fuzzy_options = selection_options_new (FUZZY_SELECT,
  650.                          fuzzy_select_options_reset);
  651.       tools_register (FUZZY_SELECT, (ToolOptions *) fuzzy_options);
  652.     }
  653.  
  654.   tool = tools_new_tool (FUZZY_SELECT);
  655.   private = g_new0 (FuzzySelect, 1);
  656.  
  657.   private->core = draw_core_new (fuzzy_select_draw);
  658.  
  659.   tool->scroll_lock = TRUE;  /*  Disallow scrolling  */
  660.  
  661.   tool->private = (void *) private;
  662.  
  663.   tool->button_press_func   = fuzzy_select_button_press;
  664.   tool->button_release_func = fuzzy_select_button_release;
  665.   tool->motion_func         = fuzzy_select_motion;
  666.   tool->arrow_keys_func     = rect_select_arrow_keys;
  667.   tool->modifier_key_func   = rect_select_modifier_update;
  668.   tool->cursor_update_func  = rect_select_cursor_update;
  669.   tool->oper_update_func    = rect_select_oper_update;
  670.   tool->control_func        = fuzzy_select_control;
  671.  
  672.   return tool;
  673. }
  674.  
  675. void
  676. tools_free_fuzzy_select (Tool *tool)
  677. {
  678.   FuzzySelect *fuzzy_sel;
  679.  
  680.   fuzzy_sel = (FuzzySelect *) tool->private;
  681.   draw_core_free (fuzzy_sel->core);
  682.   g_free (fuzzy_sel);
  683. }
  684.